/*-------------------------------------------------------------------------
 *
 * s_lock.h
 *	   硬件依赖的自旋锁实现。
 *
 *	注意：本文件中的所有宏都不打算直接调用。请通过 spin.h 中的硬件无关宏调用它们。
 *
 *	以下硬件依赖的宏必须为每个平台提供：
 *
 *	void S_INIT_LOCK(slock_t *lock)
 *		初始化自旋锁（设置为未锁定状态）。
 *
 *	int S_LOCK(slock_t *lock)
 *		获取自旋锁，如有必要则等待。
 *		如果在“合理”时间内无法获取锁，则超时并中止() --- 通常约 1 分钟。
 *		应返回“延迟”的数量；见 s_lock.c
 *
 *	void S_UNLOCK(slock_t *lock)
 *		解锁之前获取的锁。
 *
 *	bool S_LOCK_FREE(slock_t *lock)
 *		测试锁是否空闲。如果空闲则返回 true，若被锁定则返回 false。
 *		这不会改变锁的状态。
 *
 *	void SPIN_DELAY(void)
 *		延迟操作以在自旋锁等待循环内发生。
 *
 *	实现者注意：文件底部有所有这些宏的默认实现。
 *	检查你的平台是否可以使用这些或需要重写它们。
 *
 * 通常，S_LOCK() 是通过更低级别的宏 TAS() 和 TAS_SPIN() 实现的：
 *
 *	int TAS(slock_t *lock)
 *		原子测试和设置指令。尝试获取锁，但不等待。
 *		如果成功则返回 0，无法获取锁时返回非零值。
 *
 *	int TAS_SPIN(slock_t *lock)
 *		与 TAS() 相似，但在等待一个先前发现有争用的锁时使用。
 *		默认情况下，这与 TAS() 相同，但在某些架构上，最好使用一个解锁指令查询一个有争用的锁，
 *		只有在它看起来空闲时才重试原子的测试和设置。
 *
 *	TAS() 和 TAS_SPIN() 不是 API 的一部分，永远不应直接调用。
 *
 *	警告：在某些平台上，TAS() 和/或 TAS_SPIN() 有时可能在锁未锁定时报告获取锁失败。
 *	例如，在 Alpha 上，如果被中断，TAS() 将“失败”。因此，即使你确信锁是空闲的，也必须总是使用重试循环。
 *
 *	这些宏负责确保编译器不会重新排序对共享内存的访问，以便在实际获取锁之前，或在锁释放之后进行访问。
 *	在 PostgreSQL 9.5 之前，这是调用者的责任，这意味着调用者必须使用具有 volatile 限定的指针来引用自旋锁本身和在自旋锁关键区内访问的共享数据。
 *	这在语法上很尴尬，容易被遗忘（因此容易出错），并且阻止了一些有用的编译器优化。因此，我们现在要求宏本身防止编译器的重新排序，以便调用者不需要采取特殊的预防措施。
 *
 *	在具有弱内存排序的平台上，TAS()、TAS_SPIN() 和 S_UNLOCK() 宏还必须包含硬件级别的内存屏障指令，以防止在硬件级别的类似重新排序。
 *	TAS() 和 TAS_SPIN() 必须确保在获取锁之前，宏发出的加载和存储不会执行。相反，S_UNLOCK() 必须确保在释放锁之前，宏发出的加载和存储已经执行。
 *
 *	在大多数支持的平台上，TAS() 使用用汇编语言编写的 tas() 函数执行硬件原子的测试和设置指令。等效的操作系统提供的互斥例程也可以使用。
 *
 *	如果没有可用的特定于系统的 TAS()（即，HAVE_SPINLOCKS 没有定义），则我们退回到一种使用 SysV 信号量的仿真（见 spin.c）。这种仿真将比适当的 TAS() 实现慢得多，因为每次锁定或解锁都会产生一次内核调用。一份旧报告表明，当使用 SysV 信号量代码时，Postgres 大约有 40% 的时间花费在 semop(2) 中。
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *	  src/include/storage/s_lock.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef S_LOCK_H
#define S_LOCK_H

#ifdef FRONTEND
#error "s_lock.h may not be included from frontend code"
#endif

#ifdef HAVE_SPINLOCKS	/* 如果请求则跳过自旋锁 */

#if defined(__GNUC__) || defined(__INTEL_COMPILER)
/*************************************************************************
 * 所有的 gcc 内联
 * Gcc 一直将 CPU 定义为 __cpu__。
 * 其他编译器使用 __cpu 或 __cpu__，因此我们在这些情况下测试两者。
 */


/*----------
 * 标准 gcc asm 格式（假设 "volatile slock_t *lock"）：

	__asm__ __volatile__(
		"	instruction	\n"
		"	instruction	\n"
		"	instruction	\n"
:		"=r"(_res), "+m"(*lock)		// 返回寄存器，进/出锁值
:		"r"(lock)					// 锁指针，输入寄存器
:		"memory", "cc");			// 在这里显示被破坏的寄存器

 * 输出操作数列表（第一个冒号后）应始终包括
 * "+m"(*lock)，无论 asm 代码是否直接引用了这个
 * 操作数。这确保 gcc 认为锁变量中的值在
 * asm 代码中被使用和设置。此外，被破坏列表
 * （第三个冒号后）应始终包括 "memory"；这防止
 * gcc 认为它可以在 asm 代码之间缓存共享内存字段的值。
 * 如果您的 asm 代码更改了条件
 * 码寄存器，请添加 "cc"，并列出代码使用的任何临时寄存器。
 *----------
 */


#ifdef __i386__		/* 32 位 i386 */
#define HAS_TEST_AND_SET

typedef unsigned char slock_t;

#define TAS(lock) tas(lock)

static __inline__ int
tas(volatile slock_t *lock)
{
	register slock_t _res = 1;

	/*
	 * 在确认总线锁之前执行非锁定测试。请注意，
	 * 额外测试在某些 x86 平台上似乎是小损失，而在其他平台上是小
	 * 收获；并不清楚我们是否应该保留它。
	 *
	 * 当最后一次测试时，我们没有单独的 TAS() 和 TAS_SPIN()
	 * 宏。目前，最好在 TAS_SPIN() 中进行非锁定测试
	 * 而不是在 TAS() 中，就像在 x86_64 上那样，但尚未有人进行
	 * 测试来验证这一点。在没有一些经验性证据的情况下，最好
	 * 保持原样。
	 */
	__asm__ __volatile__(
		"	cmpb	$0,%1	\n"
		"	jne		1f		\n"
		"	lock			\n"
		"	xchgb	%0,%1	\n"
		"1: \n"
:		"+q"(_res), "+m"(*lock)
:		/* 无输入 */
:		"memory", "cc");
	return (int) _res;
}

#define SPIN_DELAY() spin_delay()

static __inline__ void
spin_delay(void)
{
	/*
	 * 这个序列等同于 PAUSE 指令（如果后面的指令不是字符串操作，"rep" 在旧的 IA32 处理器上会被忽略）；IA-32 架构软件开发者手册，第 3 卷，第 7.7.2 节描述了在自旋锁的内部循环中使用 PAUSE 以获得良好性能的必要性：
	 *
	 *     PAUSE 指令在执行自旋等待循环及其他一个线程在紧密轮询循环中访问共享锁或信号量的例程时，提高了支持超线程技术的 IA-32 处理器的性能。当执行自旋等待循环时，处理器在退出循环时可能会遭受严重的性能损失，因为它检测到可能的内存顺序违反并刷新核心处理器的流水线。PAUSE 指令向处理器提供了一个提示，表明代码序列是一个自旋等待循环。处理器使用这个提示来避免内存顺序违反并防止流水线刷新。此外，PAUSE 指令将自旋等待循环去流水线，以防止其过度消耗执行资源。
	 */
	__asm__ __volatile__(
		" rep; nop			\n");
}

#endif	 /* __i386__ */


#ifdef __x86_64__		/* AMD Opteron, Intel EM64T */
#define HAS_TEST_AND_SET

typedef unsigned char slock_t;

#define TAS(lock) tas(lock)

/*
 * 在 Intel EM64T 上，在真正的 xchg 之前使用非锁定测试是有益的，但仅在自旋时。
 *
 * 另见 Michael Chynoweth 和 Mary R. Lee 的《为多核 Intel(tm) EM64T 和 IA32 实现可扩展原子锁》。截至本文撰写时，可以在以下网址获取：
 * http://software.intel.com/en-us/articles/implementing-scalable-atomic-locks-for-multi-core-intel-em64t-and-ia32-architectures
 */
#define TAS_SPIN(lock)    (*(lock) ? 1 : TAS(lock))

static __inline__ int
tas(volatile slock_t *lock)
{
	register slock_t _res = 1;

	__asm__ __volatile__(
		"	lock			\n"
		"	xchgb	%0,%1	\n"
:		"+q"(_res), "+m"(*lock)
:		/* 无输入 */
:		"memory", "cc");
	return (int) _res;
}

#define SPIN_DELAY() spin_delay()

static __inline__ void
spin_delay(void)
{
	/*
	 * 在自旋延迟循环中添加 PAUSE 在 Opteron 上显然是一个无操作，但在 EM64T 上可能会有一些用处，因此我们保留它。
	 */
	__asm__ __volatile__(
		" rep; nop			\n");
}

#endif	 /* __x86_64__ */


#if defined(__ia64__) || defined(__ia64)
/*
 * Intel Itanium，gcc或Intel的编译器。
 *
 * Itanium 具有弱内存顺序，但我们依赖编译器强制执行对易失性数据的严格顺序访问。特别是，虽然 xchg 指令隐式地充当具有“获取”语义的内存屏障，但在 S_UNLOCK 宏中没有显式的内存栅栏指令。我们使用常规赋值来清除自旋锁，并相信编译器会将生成的存储指令标记为 ".rel" 操作码。
 *
 * 测试表明，在 gcc 上这一假设成立，尽管我在 gcc 手册中找不到任何显式声明。在 Intel 的编译器中，-m[no-]serialize-volatile 选项控制此行为，测试表明默认情况下启用该选项。
 *
 * 尽管 icc 在 x86[_64] 上接受 gcc asm 块，但在 ia64 上并非如此（至少在 12.x 之前的 icc 版本中）。因此，我们必须为其携带一个单独的基于编译器内在实现的实现。
 */
#define HAS_TEST_AND_SET

typedef unsigned int slock_t;

#define TAS(lock) tas(lock)

/* 在 IA64 上，在真正的 xchg 之前使用非锁定测试是有益的 */
#define TAS_SPIN(lock)	(*(lock) ? 1 : TAS(lock))

#ifndef __INTEL_COMPILER

static __inline__ int
tas(volatile slock_t *lock)
{
	long int	ret;

	__asm__ __volatile__(
		"	xchg4 	%0=%1,%2	\n"
:		"=r"(ret), "+m"(*lock)
:		"r"(1)
:		"memory");
	return (int) ret;
}

#else /* __INTEL_COMPILER */

static __inline__ int
tas(volatile slock_t *lock)
{
	int		ret;

	ret = _InterlockedExchange(lock,1);	/* 这是一个 xchg asm 宏 */

	return ret;
}

/* 在这种情况下，icc 也不能使用常规的 gcc S_UNLOCK() 宏 */
#define S_UNLOCK(lock)	\
	do { __memory_barrier(); *(lock) = 0; } while (0)

#endif /* __INTEL_COMPILER */
#endif	 /* __ia64__ || __ia64 */


/*
 * 在 ARM 和 ARM64 上，如果可用，我们使用 __sync_lock_test_and_set(int *, int)。
 *
 * 我们使用内置的整型宽度变体，因为它支持更广泛的芯片。
 */
#if defined(__arm__) || defined(__arm) || defined(__aarch64__) || defined(__aarch64)
#ifdef HAVE_GCC__SYNC_INT32_TAS
#define HAS_TEST_AND_SET

#define TAS(lock) tas(lock)

typedef int slock_t;

static __inline__ int
tas(volatile slock_t *lock)
{
	return __sync_lock_test_and_set(lock, 1);
}

#define S_UNLOCK(lock) __sync_lock_release(lock)

/*
 * 在自旋锁循环中使用 ISB 指令来延迟在高核数 ARM64 处理器上显示出好处。对较小的设备几乎没有影响，并且在 v7 之前的 ARM 芯片上根本不存在 ISB。
 */
#if defined(__aarch64__) || defined(__aarch64)

#define SPIN_DELAY() spin_delay()

static __inline__ void
spin_delay(void)
{
	__asm__ __volatile__(
		" isb;				\n");
}

#endif	 /* __aarch64__ || __aarch64 */
#endif	 /* HAVE_GCC__SYNC_INT32_TAS */
#endif	 /* __arm__ || __arm || __aarch64__ || __aarch64 */


/* S/390 和 S/390x Linux（32 位和 64 位 zSeries） */
#if defined(__s390__) || defined(__s390x__)
#define HAS_TEST_AND_SET

typedef unsigned int slock_t;

#define TAS(lock)	   tas(lock)

static __inline__ int
tas(volatile slock_t *lock)
{
	int			_res = 0;

	__asm__	__volatile__(
		"	cs 	%0,%3,0(%2)		\n"
:		"+d"(_res), "+m"(*lock)
:		"a"(lock), "d"(1)
:		"memory", "cc");
	return _res;
}

#endif	 /* __s390__ || __s390x__ */


#if defined(__sparc__)		/* Sparc */
/*
 * Solaris 一直在 TSO（总存储）模式下运行 sparc 处理器，但 Linux 以前并没有这样做，而 *BSD 仍然没有。因此，请注意获取/释放语义。CPU 将多余的内存屏障视为 NOP，因此只是代码空间。
 */
#define HAS_TEST_AND_SET

typedef unsigned char slock_t;

#define TAS(lock) tas(lock)

static __inline__ int
tas(volatile slock_t *lock)
{
	register slock_t _res;

	/*
	 *	请参见 src/backend/port/tas/sunstudio_sparc.s 中的注释，了解为什么使用 "ldstub"，该文件使用 "cas"。gcc 目前生成以 sparcv7 为目标的二进制文件，因此不可能使用 "cas"。
	 */
	__asm__ __volatile__(
		"	ldstub	[%2], %0	\n"
:		"=r"(_res), "+m"(*lock)
:		"r"(lock)
:		"memory");
#if defined(__sparcv7) || defined(__sparc_v7__)
	/*
	 * 没有可用的 stbar 或 membar，幸运的是没有实际生产的硬件需要屏障。
	 */
#elif defined(__sparcv8) || defined(__sparc_v8__)
	/* stbar 可用（并且对 PSO、RMO 都是必须的），membar 不可用 */
	__asm__ __volatile__ ("stbar	 \n":::"memory");
#else
	/*
	 * #LoadStore (RMO) | #LoadLoad (RMO) 一起是 sparcv8 及以上的适当获取屏障。
	 */
	__asm__ __volatile__ ("membar #LoadStore | #LoadLoad \n":::"memory");
#endif
	return (int) _res;
}

#if defined(__sparcv7) || defined(__sparc_v7__)
/*
 * 没有可用的 stbar 或 membar，幸运的是没有实际生产的硬件需要屏障。在这种情况下，我们回落到 gcc 的默认定义 S_UNLOCK。
 */
#elif defined(__sparcv8) || defined(__sparc_v8__)
/* stbar 可用（并且对 PSO、RMO 都是必须的），membar 不可用 */
#define S_UNLOCK(lock)	\
do \
{ \
	__asm__ __volatile__ ("stbar	 \n":::"memory"); \
	*((volatile slock_t *) (lock)) = 0; \
} while (0)
#else
/*
 * #LoadStore (RMO) | #StoreStore (RMO, PSO) 一起是 sparcv8 及以上的适当释放屏障。
	 */
#define S_UNLOCK(lock)	\
do \
{ \
	__asm__ __volatile__ ("membar #LoadStore | #StoreStore \n":::"memory"); \
	*((volatile slock_t *) (lock)) = 0; \
} while (0)
#endif

#endif	 /* __sparc__ */


/* PowerPC */
#if defined(__ppc__) || defined(__powerpc__) || defined(__ppc64__) || defined(__powerpc64__)
#define HAS_TEST_AND_SET

typedef unsigned int slock_t;

#define TAS(lock) tas(lock)

/* 在 PPC 上，在 lwarx 之前使用非锁定测试是有益的 */
#define TAS_SPIN(lock)	(*(lock) ? 1 : TAS(lock))


/*
 * addi 的第二个操作数可以是常数零或寄存器号，
 * 因此使用约束 "=&b" 以避免分配 r0。 "b" 代表 "地址
 * 基寄存器"；大多数具有这种寄存器或零属性的操作数是
 * 地址基，例如 lwax 的第二个操作数。
 *
 * 注意：根据增强型 PowerPC 架构手册，v1.0，日期为 2002 年 5 月 7 日，
 * 在 lwarx/stwcx 循环后，isync 是足够的同步屏障。
 * 在较新的机器上，我们可以使用 lwsync 以获得更好的性能。
 *
 * 通常，我们会在这里使用 GNU 风格的局部符号编写分支，
 * 即 "1f" 引用 "1:" 等等。但有些人在 AIX 上使用 gcc，
 * 并且以 IBM 的汇编器作为后端，而 IBM 的汇编器不支持局部符号。
 * 因此手动编码分支偏移量；幸运的是，所有 PPC 指令都是
 * 正好 4 字节，所以计数并不是太难。
 */
static __inline__ int
tas(volatile slock_t *lock)
{
	slock_t _t;
	int _res;

	__asm__ __volatile__(
#ifdef USE_PPC_LWARX_MUTEX_HINT
"	lwarx   %0,0,%3,1	\n"
#else
"	lwarx   %0,0,%3		\n"
#endif
"	cmpwi   %0,0		\n"
"	bne     $+16		\n"		/* 分支到 li %1,1 */
"	addi    %0,%0,1		\n"
"	stwcx.  %0,0,%3		\n"
"	beq     $+12		\n"		/* 分支到 lwsync/isync */
"	li      %1,1		\n"
"	b       $+12		\n"		/* 分支到 asm 序列的结束 */
#ifdef USE_PPC_LWSYNC
"	lwsync				\n"
#else
"	isync				\n"
#endif
"	li      %1,0		\n"

:	"=&b"(_t), "=r"(_res), "+m"(*lock)
:	"r"(lock)
:	"memory", "cc");
	return _res;
}

/*
 * PowerPC S_UNLOCK 几乎是标准的，但需要一个 "sync" 指令。
 * 在较新的机器上，我们可以使用 lwsync 以获得更好的性能。
 */
#ifdef USE_PPC_LWSYNC
#define S_UNLOCK(lock)	\
do \
{ \
	__asm__ __volatile__ ("	lwsync \n" ::: "memory"); \
	*((volatile slock_t *) (lock)) = 0; \
} while (0)
#else
#define S_UNLOCK(lock)	\
do \
{ \
	__asm__ __volatile__ ("	sync \n" ::: "memory"); \
	*((volatile slock_t *) (lock)) = 0; \
} while (0)
#endif /* USE_PPC_LWSYNC */

#endif /* powerpc */


/* Linux Motorola 68k */
#if (defined(__mc68000__) || defined(__m68k__)) && defined(__linux__)
#define HAS_TEST_AND_SET

typedef unsigned char slock_t;

#define TAS(lock) tas(lock)

static __inline__ int
tas(volatile slock_t *lock)
{
	register int rv;

	__asm__	__volatile__(
		"	clrl	%0		\n"
		"	tas		%1		\n"
		"	sne		%0		\n"
:		"=d"(rv), "+m"(*lock)
:		/* 无输入 */
:		"memory", "cc");
	return rv;
}

#endif	 /* (__mc68000__ || __m68k__) && __linux__ */


/* Motorola 88k */
#if defined(__m88k__)
#define HAS_TEST_AND_SET

typedef unsigned int slock_t;

#define TAS(lock) tas(lock)

static __inline__ int
tas(volatile slock_t *lock)
{
	register slock_t _res = 1;

	__asm__ __volatile__(
		"	xmem	%0, %2, %%r0	\n"
:		"+r"(_res), "+m"(*lock)
:		"r"(lock)
:		"memory");
	return (int) _res;
}

#endif	 /* __m88k__ */


/*
 * VAXen -- 甚至是多处理器的
 * (感谢 Tom Ivar Helbekkmo)
 */
#if defined(__vax__)
#define HAS_TEST_AND_SET

typedef unsigned char slock_t;

#define TAS(lock) tas(lock)

static __inline__ int
tas(volatile slock_t *lock)
{
	register int	_res;

	__asm__ __volatile__(
		"	movl 	$1, %0			\n"
		"	bbssi	$0, (%2), 1f	\n"
		"	clrl	%0				\n"
		"1: \n"
:		"=&r"(_res), "+m"(*lock)
:		"r"(lock)
:		"memory");
	return _res;
}

#endif	 /* __vax__ */


#if defined(__mips__) && !defined(__sgi)	/* 非SGI MIPS */
#define HAS_TEST_AND_SET

typedef unsigned int slock_t;

#define TAS(lock) tas(lock)

/*
 * 原始的MIPS-I处理器缺乏LL/SC指令，但如果我们很不幸
 * 在其中之一上运行， 我们期望内核能处理非法指令陷阱并为我们模拟它们。
 * 在更新的任何东西上（实际上，MIPS-I已经灭绝），LL/SC是唯一合理的选择，
 * 因为任何其他同步方法必然涉及内核调用。不幸的是，许多工具链仍然将MIPS-I
 * 作为代码生成目标；如果符号__mips显示确实如此，我们必须强制汇编器接受LL/SC。
 *
 * R10000及更高版本的处理器需要单独的SYNC，这与LL/SC有相同的问题。
 */
#if __mips < 2
#define MIPS_SET_MIPS2	"       .set mips2          \n"
#else
#define MIPS_SET_MIPS2
#endif

static __inline__ int
tas(volatile slock_t *lock)
{
	register volatile slock_t *_l = lock;
	register int _res;
	register int _tmp;

	__asm__ __volatile__(
		"       .set push           \n"
		MIPS_SET_MIPS2
		"       .set noreorder      \n"
		"       .set nomacro        \n"
		"       ll      %0, %2      \n"
		"       or      %1, %0, 1   \n"
		"       sc      %1, %2      \n"
		"       xori    %1, 1       \n"
		"       or      %0, %0, %1  \n"
		"       sync                \n"
		"       .set pop              "
:		"=&r" (_res), "=&r" (_tmp), "+R" (*_l)
:		/* 无输入 */
:		"memory");
	return _res;
}

/* MIPS S_UNLOCK几乎是标准，但需要一个“sync”指令 */
#define S_UNLOCK(lock)	\
do \
{ \
	__asm__ __volatile__( \
		"       .set push           \n" \
		MIPS_SET_MIPS2 \
		"       .set noreorder      \n" \
		"       .set nomacro        \n" \
		"       sync                \n" \
		"       .set pop              " \
:		/* 无输出 */ \
:		/* 无输入 */	\
:		"memory"); \
	*((volatile slock_t *) (lock)) = 0; \
} while (0)

#endif /* __mips__ && !__sgi */


#if defined(__m32r__) && defined(HAVE_SYS_TAS_H)	/* Renesas的M32R */
#define HAS_TEST_AND_SET

#include <sys/tas.h>

typedef int slock_t;

#define TAS(lock) tas(lock)

#endif /* __m32r__ */


#if defined(__sh__)				/* Renesas的SuperH */
#define HAS_TEST_AND_SET

typedef unsigned char slock_t;

#define TAS(lock) tas(lock)

static __inline__ int
tas(volatile slock_t *lock)
{
	register int _res;

	/*
	 * 这个汇编代码假设%0可以是任何寄存器，但实际上SuperH限制xor-immediate的目标为R0。
	 * 这通过对_res的“z”约束来处理。
	 */
	__asm__ __volatile__(
		"	tas.b @%2    \n"
		"	movt  %0     \n"
		"	xor   #1,%0  \n"
:		"=z"(_res), "+m"(*lock)
:		"r"(lock)
:		"memory", "t");
	return _res;
}

#endif	 /* __sh__ */


/* 这些位于s_lock.c中，但仅适用于gcc */


#if defined(__m68k__) && !defined(__linux__)	/* 非Linux摩托罗拉68k */
#define HAS_TEST_AND_SET

typedef unsigned char slock_t;
#endif


/*
 * 如果我们没有特定于平台的知识，但我们发现编译器提供了
 * __sync_lock_test_and_set()，就使用它。如果我们同时有int宽度
 * 和char宽度的版本，优先选择int宽度的版本，基于相当可疑的理由，
 * 认为这在ARM生态系统中更有可能工作。（但我们在上面处理了ARM。）
 */
#if !defined(HAS_TEST_AND_SET)

#if defined(HAVE_GCC__SYNC_INT32_TAS)
#define HAS_TEST_AND_SET

#define TAS(lock) tas(lock)

typedef int slock_t;

static __inline__ int
tas(volatile slock_t *lock)
{
	return __sync_lock_test_and_set(lock, 1);
}

#define S_UNLOCK(lock) __sync_lock_release(lock)

#elif defined(HAVE_GCC__SYNC_CHAR_TAS)
#define HAS_TEST_AND_SET

#define TAS(lock) tas(lock)

typedef char slock_t;

static __inline__ int
tas(volatile slock_t *lock)
{
	return __sync_lock_test_and_set(lock, 1);
}

#define S_UNLOCK(lock) __sync_lock_release(lock)

#endif	 /* HAVE_GCC__SYNC_INT32_TAS */

#endif	/* !defined(HAS_TEST_AND_SET) */


/*
 * gcc/icc的S_UNLOCK()默认实现。
 *
 * 注意，这个实现对任何能够在后一个存储后重新排序内存访问（无论是加载还是存储）的平台来说是不安全的。
 * 这一点在x86和大多数传统架构中不可能（有些是单处理器的），但许多现代系统有较弱的内存排序。
 * 那些这样做的必须定义自己的S_UNLOCK()版本，而不是依赖于这个。
 */
#if !defined(S_UNLOCK)
#define S_UNLOCK(lock)	\
	do { __asm__ __volatile__("" : : : "memory");  *(lock) = 0; } while (0)
#endif

#endif	/* defined(__GNUC__) || defined(__INTEL_COMPILER) */



/*
 * ---------------------------------------------------------------------
 * 使用非gcc内联汇编的平台：
 * ---------------------------------------------------------------------
 */

#if !defined(HAS_TEST_AND_SET)	/* 我们没有上面触发，让我们在这里尝试 */


#if defined(__hppa) || defined(__hppa__)	/* HP PA-RISC, GCC 和 HP 编译器 */
/*
 * HP的PA-RISC
 *
 * 有关LDCWX的详细信息，请参见src/backend/port/hpux/tas.c.template。
 * 由于LDCWX需要16字节对齐的地址，我们将slock_t声明为一个16字节的结构体。
 * 结构体中的活动字是对齐地址的那个；其他三个字只是坐落在-1上。
 *
 * 使用gcc时，我们可以内联所需的汇编代码。
 */
#define HAS_TEST_AND_SET

typedef struct
{
	int			sema[4];
} slock_t;

#define TAS_ACTIVE_WORD(lock)	((volatile int *) (((uintptr_t) (lock) + 15) & ~15))

#if defined(__GNUC__)

static __inline__ int
tas(volatile slock_t *lock)
{
	volatile int *lockword = TAS_ACTIVE_WORD(lock);
	register int lockval;

	__asm__ __volatile__(
		"	ldcwx	0(0,%2),%0	\n"
:		"=r"(lockval), "+m"(*lockword)
:		"r"(lockword)
:		"memory");
	return (lockval == 0);
}

/*
 * hppa实现不符合此文件的规则，提供了一个gcc特定的实现，
 * 位于上述定义的__GNUC__之外。这样做是为了避免HP编译器和gcc之间的重复。
 * 因此，取消定义上述的通用回退S_UNLOCK。
 */
#ifdef S_UNLOCK
#undef S_UNLOCK
#endif
#define S_UNLOCK(lock)	\
	do { \
		__asm__ __volatile__("" : : : "memory"); \
		*TAS_ACTIVE_WORD(lock) = -1; \
	} while (0)

#endif /* __GNUC__ */

#define S_INIT_LOCK(lock) \
	do { \
		volatile slock_t *lock_ = (lock); \
		lock_->sema[0] = -1; \
		lock_->sema[1] = -1; \
		lock_->sema[2] = -1; \
		lock_->sema[3] = -1; \
	} while (0)

#define S_LOCK_FREE(lock)	(*TAS_ACTIVE_WORD(lock) != 0)

#endif	 /* __hppa || __hppa__ */


#if defined(__hpux) && defined(__ia64) && !defined(__GNUC__)
/*
 * Itanium上的HP-UX，非gcc/icc编译器
 *
 * 我们假设编译器执行对易失性数据加载/存储的严格排序（参见本文件早前gcc版本的注释）。
 * 请注意，如果您在HP-UX编译器上使用+Ovolatile=__unordered选项，则此假设*无效*，所以不要这样做。
 *
 * 有关更多信息，请参见Tor Ekqvist和David Graves的“在Intel Itanium架构和PA-RISC上实现自旋锁”。
 * 截至本文撰写时，手册的1.0版可在以下网址获取：
 * http://h21007.www2.hp.com/portal/download/files/unprot/itanium/spinlocks.pdf
 */
#define HAS_TEST_AND_SET

typedef unsigned int slock_t;

#include <ia64/sys/inline.h>
#define TAS(lock) _Asm_xchg(_SZ_W, lock, 1, _LDHINT_NONE)
/* 在 IA64 上，在真正的 xchg 之前使用非锁定测试是有益的 */
#define TAS_SPIN(lock)	(*(lock) ? 1 : TAS(lock))
#define S_UNLOCK(lock)	\
	do { _Asm_mf(); (*(lock)) = 0; } while (0)

#endif	/* HPUX在IA64上，非gcc/icc */

#if defined(_AIX)	/* AIX */
/*
 * AIX (POWER)
 */
#define HAS_TEST_AND_SET

#include <sys/atomic_op.h>

typedef int slock_t;

#define TAS(lock)			_check_lock((slock_t *) (lock), 0, 1)
#define S_UNLOCK(lock)		_clear_lock((slock_t *) (lock), 0)
#endif	 /* _AIX */


/* 这些在sunstudio_(sparc|x86).s中 */

#if defined(__SUNPRO_C) && (defined(__i386) || defined(__x86_64__) || defined(__sparc__) || defined(__sparc))
#define HAS_TEST_AND_SET

#if defined(__i386) || defined(__x86_64__) || defined(__sparcv9) || defined(__sparcv8plus)
typedef unsigned int slock_t;
#else
typedef unsigned char slock_t;
#endif

extern slock_t pg_atomic_cas(volatile slock_t *lock, slock_t with,
									  slock_t cmp);

#define TAS(a) (pg_atomic_cas((a), 1, 0) != 0)
#endif


#ifdef _MSC_VER
typedef LONG slock_t;

#define HAS_TEST_AND_SET
#define TAS(lock) (InterlockedCompareExchange(lock, 1, 0))

#define SPIN_DELAY() spin_delay()

/* 如果在Win64上使用Visual C++，内联汇编不可用。
 * 使用_mm_pause内在而不是rep nop。
 */
#if defined(_WIN64)
static __forceinline void
spin_delay(void)
{
	_mm_pause();
}
#else
static __forceinline void
spin_delay(void)
{
	/* 请参阅 gcc 代码的注释。相同的代码，MASM 语法 */
	__asm rep nop;
}
#endif

#include <intrin.h>
#pragma intrinsic(_ReadWriteBarrier)

#define S_UNLOCK(lock)	\
	do { _ReadWriteBarrier(); (*(lock)) = 0; } while (0)

#endif


#endif	/* !defined(HAS_TEST_AND_SET) */


/* 如果我们没有任何方法来做自旋锁，则爆炸 */
#ifndef HAS_TEST_AND_SET
#error PostgreSQL does not have native spinlock support on this platform.  To continue the compilation, rerun configure using --disable-spinlocks.  However, performance will be poor.  Please report this to pgsql-bugs@lists.postgresql.org.
#endif


#else	/* !HAVE_SPINLOCKS */


/*
 * 使用信号量的伪自旋锁实现 - 慢且容易受到内核信号量数量限制的影响，
 * 所以除非必须，否则不要使用这个！子例程出现在spin.c中。
 */
typedef int slock_t;

extern bool s_lock_free_sema(volatile slock_t *lock);
extern void s_unlock_sema(volatile slock_t *lock);
extern void s_init_lock_sema(volatile slock_t *lock, bool nested);
extern int	tas_sema(volatile slock_t *lock);

#define S_LOCK_FREE(lock)	s_lock_free_sema(lock)
#define S_UNLOCK(lock)	 s_unlock_sema(lock)
#define S_INIT_LOCK(lock)	s_init_lock_sema(lock, false)
#define TAS(lock)	tas_sema(lock)


#endif	/* HAVE_SPINLOCKS */


/*
 * 默认定义 - 根据需要覆盖这些定义。
 */

#if !defined(S_LOCK)
#define S_LOCK(lock) \
	(TAS(lock) ? s_lock((lock), __FILE__, __LINE__, PG_FUNCNAME_MACRO) : 0)
#endif	 /* S_LOCK */

#if !defined(S_LOCK_FREE)
#define S_LOCK_FREE(lock)	(*(lock) == 0)
#endif	 /* S_LOCK_FREE */

#if !defined(S_UNLOCK)
/*
 * 我们的 S_UNLOCK 的默认实现基本上是 *(lock) = 0。 这在平台可以重排序内存访问（无论是加载还是存储）之后的存储时是不安全的；能够这样做的平台必须定义自己的 S_UNLOCK。 但 CPU 重排序并不是唯一的问题：如果我们简单地将 S_UNLOCK() 定义为一个内联宏，编译器可能会将关键区块内部的指令重排序到锁释放之后。 由于编译器可能无法知道外部函数 s_unlock 在做什么，把相同的逻辑放在那里应该是足够的。 然而，一个足够智能的全局优化编译器可能会打破这一假设，对于每个自旋锁释放的函数调用的开销可能会显著影响性能，因此我们仅在我们不知道适当内在的情况下才使用此实现。 在大多数情况下，那些是相对不常见的平台/编译器组合，PostgreSQL 项目无法访问。
*/
#define USE_DEFAULT_S_UNLOCK
extern void s_unlock(volatile slock_t *lock);
#define S_UNLOCK(lock)		s_unlock(lock)
#endif	 /* S_UNLOCK */

#if !defined(S_INIT_LOCK)
#define S_INIT_LOCK(lock)	S_UNLOCK(lock)
#endif	 /* S_INIT_LOCK */

#if !defined(SPIN_DELAY)
#define SPIN_DELAY()	((void) 0)
#endif	 /* SPIN_DELAY */

#if !defined(TAS)
extern int	tas(volatile slock_t *lock);		/* 在 port/.../tas.s，或
												 * s_lock.c */

#define TAS(lock)		tas(lock)
#endif	 /* TAS */

#if !defined(TAS_SPIN)
#define TAS_SPIN(lock)	TAS(lock)
#endif	 /* TAS_SPIN */

extern PGDLLIMPORT slock_t dummy_spinlock;

/*
 * 平台无关的外部支持例程
*/
extern int s_lock(volatile slock_t *lock, const char *file, int line, const char *func);

/* 支持动态调整 spins_per_delay */
#define DEFAULT_SPINS_PER_DELAY  100

extern void set_spins_per_delay(int shared_spins_per_delay);
extern int	update_spins_per_delay(int shared_spins_per_delay);

/*
 * 支持自旋延迟，这在各种进行自旋锁类程序的地方都很有用。
*/
typedef struct
{
	int			spins;
	int			delays;
	int			cur_delay;
	const char *file;
	int			line;
	const char *func;
} SpinDelayStatus;

static inline void
init_spin_delay(SpinDelayStatus *status,
				const char *file, int line, const char *func)
{
	status->spins = 0;
	status->delays = 0;
	status->cur_delay = 0;
	status->file = file;
	status->line = line;
	status->func = func;
}

#define init_local_spin_delay(status) init_spin_delay(status, __FILE__, __LINE__, PG_FUNCNAME_MACRO)
extern void perform_spin_delay(SpinDelayStatus *status);
extern void finish_spin_delay(SpinDelayStatus *status);

#endif	 /* S_LOCK_H */
